package jf.jextn.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import jf.jextn.JExtn;

public class ReflectionUtil {

    public static Method findMethod(Class<?> type, String methodName, Class<?>... argsTypes) {
        Class<?>[] argsType = JExtn.ifNull(argsTypes, new Class<?>[0]);
        try {
            Method method = type.getMethod(methodName, argsType);

            return method;
        } catch (SecurityException e) {
            throw new IllegalStateException(String.format("'{0}' Method not accessable", methodName), e);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(String.format("'{0}' Method not found", methodName), e);
        }
    }

    public static String getShortClassName(Class<?> className) {
        return className.getSimpleName();
    }

    public static String getShortClassName(String className) {
        int index = className.lastIndexOf('.');

        return index > 0 ? className.substring(index + 1) : className;
    }

    public static String getGenericInfo(Class<?> type) {
        StringBuilder sb = new StringBuilder();

        sb.append(" \n Type                 :").append(type);

        sb.append(" \n Generic super class  :").append(type.getGenericSuperclass());
        if (type.getGenericSuperclass() != null) {
            sb.append(" (").append((type.getGenericSuperclass().getClass())).append(" )");
        }
        sb.append(" \n Type Parameters      :").append(Arrays.toString(type.getTypeParameters()));
        sb.append(" \n Generic Interfaces   :").append(Arrays.toString(type.getGenericInterfaces()));
        sb.append(" \n Type Parameters       :").append(Arrays.toString(type.getTypeParameters()));

        return sb.toString();
    }

    public static Object newInstance(String clazz, Class<?>[] types, Object[] args) {
        Object instance = null;
        try {
            Class<?> clz = Class.forName(clazz);
            Constructor<?> constructor = clz.getConstructor(types);
            instance = constructor.newInstance(args);

        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Invalid class " + clazz, e);
        } catch (SecurityException e) {
            throw new IllegalStateException("Not able to access " + clazz, e);
        } catch (NoSuchMethodException e) {
            String type = Arrays.toString(types);
            throw new IllegalArgumentException("class " + clazz + "do not have construtor (" + type + " )", e);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("parameter type and actaul type mismatch", e);
        } catch (InstantiationException e) {
            throw new IllegalStateException(e);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        } catch (InvocationTargetException e) {
            throw new IllegalStateException(e);
        }

        return instance;
    }

    public static <I extends T, T> Class<?>[] getActualTypesOfTypeArguments(Class<I> impl, Class<T> targetType) {
        return TypeResolver.resolveArguments(impl, targetType);
    }
}